package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.core.exception.ConnectionException;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.core.domain.R;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IDeviceDataService;
import com.zzyl.nursing.service.IDeviceService;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.DeviceVo;
import com.zzyl.nursing.vo.ProductVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.*;

/**
 * 设备Service业务层处理
 */
@Slf4j
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private IoTDAClient client;

    @Autowired
    private IDeviceDataService deviceDataService;

    /**
     * 查询设备列表
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 从物联网平台同步产品列表
     */
    @Override
    public void syncProductList() {
        ListProductsRequest listProductsRequest = new ListProductsRequest();
        listProductsRequest.setLimit(50);
        ListProductsResponse response = client.listProducts(listProductsRequest);
        List<ProductSummary> products = response.getProducts();
        // 将数据保存到redis中
        redisTemplate.opsForValue().set(CacheConstants.IOT_ALL_PRODUCT_LIST, JSONUtil.toJsonStr(products));
    }

    /**
     * 查询所有产品列表
     */
    @Override
    public List<ProductVo> allProduct() {
        // 从redis中查询数据
        String jsonStr = redisTemplate.opsForValue().get(CacheConstants.IOT_ALL_PRODUCT_LIST);
        // 如果数据为空，则返回一个空集合
        if (StrUtil.isEmpty(jsonStr)) {
            return Collections.emptyList();
        }
        // 解析数据，并返回
        return JSONUtil.toList(jsonStr, ProductVo.class);
    }

    /**
     * 注册设备
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(DeviceDto deviceDto) {
        // 1. 参数校验
        validateDeviceDto(deviceDto);

        // 2. 唯一性校验（新增专用）
        validateForCreate(deviceDto);

        // 3. 注册到华为IoT平台
        AddDeviceResponse response = registerIotDevice(deviceDto);

        // 4. 保存到本地数据库
        saveLocalDevice(deviceDto, response);
    }

    /**
     * 查询设备详细信息
     */
    @Override
    public AjaxResult detail(String iotId) {
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.withDeviceId(iotId);
        ShowDeviceResponse response;
        try {
            response = client.showDevice(request);
        } catch (Exception e) {
            throw new BaseException("查询IOT设备数据失败");
        }
        LambdaQueryWrapper<Device> query = new LambdaQueryWrapper<>();
        query.eq(Device::getIotId, iotId);
        Device device = getOne(query);
        DeviceVo deviceVo = new DeviceVo();
        if(ObjUtil.isNotNull(device)){
            deviceVo = BeanUtil.toBean(device, DeviceVo.class);
        }
        deviceVo.setDeviceStatus(response.getStatus());
        String activeTimeStr = response.getActiveTime();
        if(StrUtil.isNotEmpty(activeTimeStr)){
            LocalDateTime activeTime = LocalDateTimeUtil.parse(activeTimeStr, DatePattern.UTC_MS_PATTERN);
            // 日期时区转换
            activeTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                    .withZoneSameInstant(ZoneId.systemDefault())
                    .toLocalDateTime();
            deviceVo.setActiveTime(activeTime);
        }
        return AjaxResult.success(deviceVo);
    }

    /**
     * 查看设备上报的数据
     */
    @Override
    public AjaxResult queryServiceProperties(String iotId) {
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.setDeviceId(iotId);
        ShowDeviceShadowResponse response = client.showDeviceShadow(request);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("物联网接口 - 查询设备上报数据，调用失败");
        }
        List<DeviceShadowData> shadow = response.getShadow();
        if(CollUtil.isEmpty(shadow)){
            List<Object> emptyList = Collections.emptyList();
            return AjaxResult.success(emptyList);
        }
        // 返回数据
        List<DeviceDetailVo> list = new ArrayList<>();
        shadow.forEach(deviceShadowData -> {
            JSONObject jsonObject = JSONUtil.parseObj(deviceShadowData.getReported().getProperties());
            // 处理上报时间日期
            LocalDateTime activeTime =  LocalDateTimeUtil.parse(deviceShadowData.getReported().getEventTime(), "yyyyMMdd'T'HHmmss'Z'");
            // 日期时区转换
            LocalDateTime eventTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                    .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                    .toLocalDateTime();

            jsonObject.forEach((k,v)->{
                DeviceDetailVo deviceDetailVo = DeviceDetailVo
                        .builder()
                        .functionId(k)
                        .eventTime(eventTime)
                        .value(v)
                        .build();
                list.add(deviceDetailVo);
            });
        });
        return AjaxResult.success(list);
    }

    /**
     * 修改设备
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDevice(DeviceDto deviceDto) {
        if (ObjUtil.isNull(deviceDto.getId()) && StrUtil.isBlank(deviceDto.getIotId())) {
            throw new BaseException("设备ID不能为空");
        }

        // 1. 参数校验
        validateDeviceDto(deviceDto);

        // 2. 查询现有设备
        Device existingDevice = getExistingDevice(deviceDto);

        // 3. 唯一性校验（更新专用）
        validateForUpdate(deviceDto, existingDevice);

        // 4. 更新到华为IoT平台
        updateIotDevice(deviceDto, existingDevice);

        // 5. 更新到本地数据库device表
        updateLocalDevice(deviceDto, existingDevice);

        //7. 更新到本地数据库device_data表
        LambdaUpdateWrapper<DeviceData> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(DeviceData::getIotId, existingDevice.getIotId())
                .set(DeviceData::getDeviceName, deviceDto.getDeviceName())
                .set(DeviceData::getAccessLocation, deviceDto.getRemark())
                .set(DeviceData::getLocationType, deviceDto.getLocationType())
                .set(DeviceData::getPhysicalLocationType, deviceDto.getPhysicalLocationType());

        deviceDataService.update(updateWrapper);
        /*LambdaQueryWrapper<DeviceData> query = new LambdaQueryWrapper<>();
        query.eq(DeviceData::getIotId, existingDevice.getIotId());
        List<DeviceData> deviceDataList = deviceDataService.list(query);
        if(CollUtil.isNotEmpty(deviceDataList)){
            deviceDataList.forEach(deviceData -> {
                deviceData.setDeviceName(deviceDto.getDeviceName());
                deviceData.setAccessLocation(deviceDto.getRemark());
                deviceData.setLocationType(deviceDto.getLocationType());
                deviceData.setPhysicalLocationType(deviceDto.getPhysicalLocationType());
            });
            deviceDataService.updateBatchById(deviceDataList);
        }*/

    }

    /**
     * 删除设备  就不封装方法了,不然方法太多了
     * @param iotId
     */
    @Override
    public void deleteDevice(String iotId) {
        //1.删除Iot对应的设备
        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.withDeviceId(iotId);
        try {
            DeleteDeviceResponse response = client.deleteDevice(request);
        } catch (Exception e) {
            throw new BaseException("删除IOT设备数据失败");
        }
        //2.删除本地数据库对应的设备
        LambdaQueryWrapper<Device> query = new LambdaQueryWrapper<>();
        query.eq(Device::getIotId, iotId);
        try {
            remove(query);
        } catch (Exception e) {
            throw new BaseException("删除本地设备数据失败");
        }

        //TODO 3.删除本地数据库对应的设备数据
    }

    @Override
    public R<List<ServiceCapability>> queryproduct(String productKey) {
        ShowProductRequest request = new ShowProductRequest();
        request.withProductId(productKey);
        try {
            ShowProductResponse response = client.showProduct(request);
            if(response.getHttpStatusCode() != 200){
                return R.fail("物联网接口 - 查询产品服务列表，调用失败");
            }
            List<ServiceCapability> list = response.getServiceCapabilities();
            return R.ok(list);
        } catch (ConnectionException e) {
            return R.fail("物联网接口 - 查询产品服务列表，调用失败");
        }
    }

    /**
     * 参数校验
     */
    private void validateDeviceDto(DeviceDto deviceDto) {
        if (ObjUtil.isNull(deviceDto)) {
            throw new BaseException("设备信息不能为空");
        }
        if (StrUtil.isBlank(deviceDto.getDeviceName())) {
            throw new BaseException("设备名称不能为空");
        }
        if (StrUtil.isBlank(deviceDto.getNodeId())) {
            throw new BaseException("设备标识不能为空");
        }
        if (StrUtil.isBlank(deviceDto.getProductKey())) {
            throw new BaseException("产品密钥不能为空");
        }

        // 设置默认值
        if (ObjUtil.isNull(deviceDto.getPhysicalLocationType())) {
            deviceDto.setPhysicalLocationType(-1);
        }
    }

    /**
     * 新增时的唯一性校验
     */
    private void validateForCreate(DeviceDto deviceDto) {
        // 设备名称是否重复
        checkNameExist(deviceDto.getDeviceName(), null);

        // 设备标识是否重复
        checkNodeIdExist(deviceDto.getNodeId(), null);

        // 同一位置是否绑定相同的产品
        checkSameProductExist(deviceDto, null);
    }

    /**
     * 更新时的唯一性校验
     */
    private void validateForUpdate(DeviceDto deviceDto, Device existingDevice) {
        // 设备名称是否重复（排除自身）
        if (!deviceDto.getDeviceName().equals(existingDevice.getDeviceName())) {
            //先排除自身,再去走新增的校验,余下同理
            checkNameExist(deviceDto.getDeviceName(), existingDevice.getId());
        }

        // 设备标识是否重复（排除自身）
        /*if (!deviceDto.getNodeId().equals(existingDevice.getNodeId())) {
            checkNodeIdExist(deviceDto.getNodeId(), existingDevice.getId());
        }*/

        // 同一位置是否绑定相同的产品（排除自身）
        if (isLocationChanged(deviceDto, existingDevice)) {
            checkSameProductExist(deviceDto, existingDevice.getId());
        }
    }

    /**
     * 检查位置是否发生变化
     */
    private boolean isLocationChanged(DeviceDto deviceDto, Device existingDevice) {
        return !Objects.equals(deviceDto.getBindingLocation(), existingDevice.getBindingLocation()) ||
                !Objects.equals(deviceDto.getLocationType(), existingDevice.getLocationType()) ||
                !Objects.equals(deviceDto.getPhysicalLocationType(), existingDevice.getPhysicalLocationType()) ||
                !Objects.equals(deviceDto.getProductKey(), existingDevice.getProductKey());
    }

    /**
     * 获取现有设备信息
     */
    private Device getExistingDevice(DeviceDto deviceDto) {
        Device existingDevice;
        if (ObjUtil.isNotNull(deviceDto.getId())) {
            //1.通过id查询
            existingDevice = getById(deviceDto.getId());
        } else {
            //2.这里我也不好说,如果没有id应该抛出异常才对,但是想了一下再用设备id查一下,万一呢
            LambdaQueryWrapper<Device> query = new LambdaQueryWrapper<>();
            query.eq(Device::getIotId, deviceDto.getIotId());
            existingDevice = getOne(query);
        }

        if (ObjUtil.isNull(existingDevice)) {
            throw new BaseException("设备不存在");
        }
        return existingDevice;
    }

    /**
     * 检查设备名称是否存在
     */
    private void checkNameExist(String deviceName, Long excludeId) {
        if (StrUtil.isBlank(deviceName)) {
            throw new BaseException("设备名称不能为空");
        }

        LambdaQueryWrapper<Device> query = new LambdaQueryWrapper<>();
        query.eq(Device::getDeviceName, deviceName);

        if (ObjUtil.isNotNull(excludeId)) {
            query.ne(Device::getId, excludeId);
        }

        if (count(query) > 0) {
            throw new BaseException("设备名称已存在: " + deviceName);
        }
    }

    /**
     * 检查设备标识是否存在
     */
    private void checkNodeIdExist(String nodeId, Long excludeId) {
        if (StrUtil.isBlank(nodeId)) {
            throw new BaseException("设备标识不能为空");
        }

        LambdaQueryWrapper<Device> query = new LambdaQueryWrapper<>();
        query.eq(Device::getNodeId, nodeId);

        if (ObjUtil.isNotNull(excludeId)) {
            query.ne(Device::getId, excludeId);
        }

        if (count(query) > 0) {
            throw new BaseException("设备标识已存在: " + nodeId);
        }
    }

    /**
     * 检查同一位置是否绑定相同的产品
     */
    private void checkSameProductExist(DeviceDto deviceDto, Long excludeId) {
        LambdaQueryWrapper<Device> query = new LambdaQueryWrapper<>();
        query.eq(Device::getBindingLocation, deviceDto.getBindingLocation())
                .eq(Device::getProductKey, deviceDto.getProductKey())
                .eq(Device::getLocationType, deviceDto.getLocationType())
                .eq(Device::getPhysicalLocationType, deviceDto.getPhysicalLocationType());

        if (ObjUtil.isNotNull(excludeId)) {
            query.ne(Device::getId, excludeId);
        }

        if (count(query) > 0) {
            throw new BaseException("同一位置已绑定相同产品");
        }
    }

    /**
     * 保存设备到本地数据库
     */
    private void saveLocalDevice(DeviceDto deviceDto, AddDeviceResponse response) {
        Device device = BeanUtil.copyProperties(deviceDto, Device.class);
        device.setIotId(response.getDeviceId());
        device.setSecret(response.getAuthInfo().getSecret());
        device.setHaveEntranceGuard(deviceDto.getPhysicalLocationType().equals(-1) ? 0 : 1);

        try {
            save(device);
        } catch (Exception e) {
            log.error("添加设备到本地失败: {}", deviceDto.getDeviceName(), e);
            throw new BaseException("添加设备到本地失败");
        }
    }

    /**
     * 注册设备到IOT平台
     */
    private AddDeviceResponse registerIotDevice(DeviceDto deviceDto) {
        AddDeviceRequest request = new AddDeviceRequest();
        AddDevice body = new AddDevice();
        AuthInfo authInfobody = new AuthInfo();
        String secret = UUID.randomUUID().toString().replace("-", "");

        authInfobody.withAuthType("SECRET")
                .withSecret(secret)
                .withSecureAccess(true);

        body.withDescription(deviceDto.getDeviceDescription())
                .withAuthInfo(authInfobody)
                .withProductId(deviceDto.getProductKey())
                .withDeviceName(deviceDto.getDeviceName())
                .withNodeId(deviceDto.getNodeId());

        request.withBody(body);

        AddDeviceResponse response;
        try {
            response = client.addDevice(request);
            if (response.getHttpStatusCode() != 201) {
                throw new BaseException("物联网平台注册设备失败");
            }
        } catch (Exception e) {
            log.error("物联网接口 - 注册设备失败: {}", deviceDto.getDeviceName(), e);
            throw new BaseException("物联网接口 - 注册设备失败: " + e.getMessage());
        }
        return response;
    }

    /**
     * 更新本地设备信息
     */
    private void updateLocalDevice(DeviceDto deviceDto, Device existingDevice) {
        Device device = BeanUtil.copyProperties(deviceDto, Device.class);
        device.setId(existingDevice.getId());
        device.setIotId(existingDevice.getIotId()); // 保持原有iotId
        device.setSecret(existingDevice.getSecret()); // 保持原有secret
        device.setHaveEntranceGuard(deviceDto.getPhysicalLocationType().equals(-1) ? 0 : 1);

        try {
            updateById(device);
        } catch (Exception e) {
            log.error("更新设备到本地失败: {}", deviceDto.getDeviceName(), e);
            throw new BaseException("更新设备到本地失败");
        }
    }

    /**
     * 更新IoT设备信息
     */
    private void updateIotDevice(DeviceDto deviceDto, Device existingDevice) {
        try {
            UpdateDeviceRequest request = new UpdateDeviceRequest();
            request.withDeviceId(existingDevice.getIotId());

            UpdateDevice body = new UpdateDevice();
            body.withDescription(deviceDto.getDeviceDescription());
            body.withDeviceName(deviceDto.getDeviceName());

            request.withBody(body);

            UpdateDeviceResponse response = client.updateDevice(request);
            if (response.getHttpStatusCode() != 200) {
                throw new BaseException("物联网平台更新设备失败");
            }

        } catch (Exception e) {
            log.error("物联网接口 - 更新设备失败: {}", deviceDto.getDeviceName(), e);
            throw new BaseException("物联网接口 - 更新设备失败: " + e.getMessage());
        }
    }
}